package way.learning.zz;

import way.learning.zz.event.BeforeStartEvent;
import way.learning.zz.filter.DefaultFileFilter;
import way.learning.zz.filter.FileFilter;
import way.learning.zz.listener.Rdp4jListener;
import way.learning.zz.source.FileElement;
import way.learning.zz.source.PolledDirectory;

import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
 * 构建器类，用于配置然后返回启动的DirectoryPoller实例
 */
public final class DirectoryPollerBuilder {

    private static final String NULL_ARGUMENT_ERROR_MESSAGE = "null argument not allowed!";
    static final String DEFAULT_THREAD_NAME = "DirectoryPoller-";

    //轮询目录集合
    Set<PolledDirectory> directories = new LinkedHashSet<PolledDirectory>();

    /**
     * 可选配置项 默认值
     */
    long pollingIntervalInMillis = 1000;
    FileFilter filter =  new DefaultFileFilter();


    String threadName = DEFAULT_THREAD_NAME;
    boolean fileAddedEventEnabledForInitialContent = false;
    boolean parallelDirectoryPollingEnabled = false;

    //Listener集合
    Set<Rdp4jListener> listeners = new HashSet<Rdp4jListener>();


    //无参构造函数 package-private access only.
    DirectoryPollerBuilder() {
    }

    /**
     * 启用为DirectoryPoller中添加目录的初始内容触发FileAddedEvent事件
     */
    public DirectoryPollerBuilder enableFileAddedEventsForInitialContent() {
        fileAddedEventEnabledForInitialContent = true;
        return this;
    }

    /**
     * 启用对DirectoryPoller中添加的目录进行并行轮询
     */
    public DirectoryPollerBuilder enableParallelPollingOfDirectories() {
        parallelDirectoryPollingEnabled = true;
        return this;
    }

    /**
     * Adds the given <code>directory</code> to the list of polled directories.
     * Mandatory to add at least one directory.
     * <p>
     * Once the {@link DirectoryPoller} has been built, it can be used to add
     * additional polled directories, or remove polled directories.
     * 
     * @param directory - the directory to poll.
     * @throws NullPointerException - if the given argument is null.
     * @return {@link DirectoryPollerBuilder}
     */
    public DirectoryPollerBuilder addPolledDirectory(PolledDirectory directory) {
        if (directory == null) {
            throw new NullPointerException(NULL_ARGUMENT_ERROR_MESSAGE);
        }
        directories.add(directory);
        return this;
    }

    /**
     * Set the interval between each poll cycle. Optional parameter.
     * Default value is 1000 milliseconds.
     * 
     */
    public DirectoryPollerBuilder setPollingInterval(long interval, TimeUnit timeUnit) {
        if (interval < 0) {
            throw new IllegalArgumentException("Argument 'interval' is negative: " + interval);
        }
        pollingIntervalInMillis = timeUnit.toMillis(interval);
        return this;
    }

    /**
     * Set a FileFilter to be used. Only FileElement}'s
     * Satisfying the filter will be considered.
     *
     * Optional setting. By default all {@link FileElement}'s are satisfying the filter.
     */
    public DirectoryPollerBuilder setDefaultFileFilter(FileFilter filter) {
        if (filter == null) {
            throw new NullPointerException(NULL_ARGUMENT_ERROR_MESSAGE);
        }
        this.filter = filter;
        return this;
    }

    /**
     * Changes the name of the associated polling thread to be equal to the given name
     *
     * Optional setting. By default each thread is named "DirectoryPoller-{X}",
     * where {X} is a sequence number. I.e: "DirectoryPoller-1", "DirectoryPoller-2" etc. etc.
     *
     */
    public DirectoryPollerBuilder setThreadName(String name) {
        if (name == null) {
            String message = "Null argument";
            throw new NullPointerException(message);
        }
        threadName = name;
        return this;
    }

    /**
     * Adds the given listener to the list of  Rdp4jListener that receives notifications.
     */
    public DirectoryPollerBuilder addListener(Rdp4jListener listener) {
        if (listener == null) {
            throw new NullPointerException(NULL_ARGUMENT_ERROR_MESSAGE);
        }
        listeners.add(listener);
        return this;
    }

    /**
     * Asynchronously starts the poll-cycle mechanism.
     */
    public DirectoryPollerFuture startAsync() {
        return future();
    }


    public DirectoryPoller start() {
        //返回任务执行结果
        return future().get();
    }

    private DirectoryPollerFuture future() {

        //构建一个目录轮询器 DirectoryPoller
        final DirectoryPoller dp = new DirectoryPoller(this);

        //执行任务
        Future<DirectoryPoller> f = Util.invokeTask("DP-BeforeStart", new Callable<DirectoryPoller>() {

            //实现callable接口，执行逻辑都在call方法中
            @Override
            public DirectoryPoller call() {

                dp.notifier.beforeStart(new BeforeStartEvent());


                dp.start();
                return dp;
            }

        });

        return new DirectoryPollerFuture(f);
    }
}
